﻿// $Id: RegexRule.cs 10 2008-02-29 15:58:02Z mcartoixa $

/* ***************************************************************************
 *                                                                           *
 * Copyright © 2007 NourY Solutions                                          *
 *                                                                           *
 * This file is part of DataRules.                                           *
 *                                                                           *
 * DataRules is free software; you can redistribute it and/or modify it      *
 * under the terms of the GNU Lesser General Public License as published     *
 * by the Free Software Foundation; either version 3 of the License, or      *
 * (at your option) any later version.                                       *
 *                                                                           *
 * DataRules is distributed in the hope that it will be useful, but          *
 * WITHOUT ANY WARRANTY; without even the implied warranty of                *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          *
 * Lesser General Public License for more details.                           *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public          *
 * License along with this program.  If not, see                             *
 * <http://www.gnu.org/licenses/lgpl.html>.                                  *
 *                                                                           *
 *************************************************************************** */

/*! \file
 * \brief Definition of \c Salamanca.DataRules.RegexRule.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Salamanca.DataRules
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Business rule based on a <see cref="Regex" />.</summary>
    /// <remarks>
    ///   <para>The regular expression is matched against the <see cref="Object.ToString()">string representation</see> of the property value.</para>
    ///   <note>If the property value is <c>null</c>, the rule is considered validated.
    /// Add a <see cref="NotNullRule{T}" /> to the property if it cannot be <c>null</c>.</note>
    /// </remarks>
    /// <example>
    ///   <code>
    /// using Salamanca.DataRules;
    /// 
    /// public class City
    /// {
    ///     private string _ZipCode;
    /// 
    ///     public string ZipCode
    ///     {
    ///         get
    ///         {
    ///              return _ZipCode;
    ///         }
    ///     }
    /// 
    ///     protected List&lt;IRule&gt; CreateRules()
    ///     {
    ///         List&lt;IRule&gt; ret=new List&lt;IRule&gt;();
    /// 
    ///         ret.Add(
    ///             new RegexRule&lt;City&gt;(
    ///                 "ZipCode",
    ///                 "This is not a valid US Zip code.",
    ///                 @"^(\d{5}-\d{4})|(\d{5})$"
    ///             )
    ///         );
    /// 
    ///         ret.Add(
    ///             new NotNullRule&lt;City&gt;(
    ///                 "ZipCode"
    ///             )
    ///         );
    /// 
    ///         return ret;
    ///     }
    /// }
    ///   </code>
    /// </example>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public class RegexRule<T>:
        GenericRule<T>
    {

        /// <summary>Constructor.</summary>
        /// <param name="propertyName">The name of the property. This cannot be an empty string (<c>""</c>).</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <exception cref="ArgumentException">
        ///   <para>The property <paramref name="propertyName" /> cannot be found on the specified <typeparamref name="T">type</typeparamref>.</para>
        ///   <para>The property <paramref name="propertyName" /> is not readable.</para>
        ///   <para>The property <paramref name="propertyName" /> is an indexer.</para>
        ///   <para>Regular expression parsing error.</para>
        /// </exception>
        /// <exception cref="ArgumentNullException">
        ///   <para><paramref name="propertyName" /> is <c>null</c>.</para>
        ///   <para><paramref name="pattern" /> is <c>null</c>.</para>
        /// </exception>
        public RegexRule(string propertyName, string pattern):
            this(propertyName, Resources.SR.RegexRuleDescription, pattern)
        {
        }

        /// <summary>Constructor.</summary>
        /// <param name="propertyName">The name of the property. This cannot be an empty string (<c>""</c>).</param>
        /// <param name="description">The description of the rule.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <exception cref="ArgumentException">
        ///   <para>The property <paramref name="propertyName" /> cannot be found on the specified <typeparamref name="T">type</typeparamref>.</para>
        ///   <para>The property <paramref name="propertyName" /> is not readable.</para>
        ///   <para>The property <paramref name="propertyName" /> is an indexer.</para>
        ///   <para>Regular expression parsing error.</para>
        /// </exception>
        /// <exception cref="ArgumentNullException">
        ///   <para><paramref name="propertyName" /> is <c>null</c>.</para>
        ///   <para><paramref name="description" /> is <c>null</c>.</para>
        ///   <para><paramref name="pattern" /> is <c>null</c>.</para>
        /// </exception>
        public RegexRule(string propertyName, string description, string pattern):
            this(propertyName, description, pattern, RegexOptions.None)
        {
        }

        /// <summary>Constructor.</summary>
        /// <param name="propertyName">The name of the property. This cannot be an empty string (<c>""</c>).</param>
        /// <param name="description">The description of the rule.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <param name="options">A bitwise OR combination of <see cref="RegexOptions" /> enumeration values.</param>
        /// <exception cref="ArgumentException">
        ///   <para>The property <paramref name="propertyName" /> cannot be found on the specified <typeparamref name="T">type</typeparamref>.</para>
        ///   <para>The property <paramref name="propertyName" /> is not readable.</para>
        ///   <para>The property <paramref name="propertyName" /> is an indexer.</para>
        ///   <para>Regular expression parsing error.</para>
        /// </exception>
        /// <exception cref="ArgumentNullException">
        ///   <para><paramref name="propertyName" /> is <c>null</c>.</para>
        ///   <para><paramref name="description" /> is <c>null</c>.</para>
        ///   <para><paramref name="pattern" /> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <para><paramref name="options" /> contains an invalid flag.</para>
        /// </exception>
        public RegexRule(string propertyName, string description, string pattern, RegexOptions options):
            base(propertyName, description)
        {
            _Regex=new Regex(pattern, options);
        }

        /// <summary>Validates the rule on the specified target.</summary>
        /// <remarks>
        ///   <para>The regular expression is matched against the <see cref="Object.ToString()">string representation</see> of the <see cref="GenericRule{T}.Property" /> value.</para>
        ///   <note>If the <see cref="GenericRule{T}.Property" /> value is <c>null</c>, this method returns <c>true</c>.
        /// Add a <see cref="NotNullRule{T}" /> to the <see cref="GenericRule{T}.Property" /> if it cannot be <c>null</c>.</note>
        /// </remarks>
        /// <param name="target">The instance against which the rule is validated.</param>
        /// <returns><c>true</c> if the instance is valid for the rule, <c>false</c> if not.</returns>
        protected override bool DoValidate(object target)
        {
            object v=Property.GetValue(target, null);
            if (v==null)
                return true;

            return _Regex.IsMatch(v.ToString());
        }

        /// <summary>The regular expression.</summary>
        private Regex _Regex;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Business rule attribute based on a <see cref="Regex" />.</summary>
    /// <remarks>
    ///   <para>The regular expression is matched against the <see cref="Object.ToString()">string representation</see> of the property value.</para>
    ///   <note>If the property value is <c>null</c>, the rule is considered validated.
    /// Add a <see cref="NotNullRule{T}" /> to the property if it cannot be <c>null</c>.</note>
    /// </remarks>
    /// <example>
    ///   <code>
    /// using Salamanca.DataRules;
    /// 
    /// public class City
    /// {
    ///     private string _ZipCode;
    /// 
    ///     [RegexRule("This is not a valid US Zip code.", @"^(\d{5}-\d{4})|(\d{5})$")]
    ///     [NotNullRule]
    ///     public string ZipCode
    ///     {
    ///         get
    ///         {
    ///              return _ZipCode;
    ///         }
    ///     }
    /// 
    ///     protected List&lt;IRule&gt; CreateRules()
    ///     {
    ///         return Rules.CreateAttributedRule(GetType());
    ///     }
    /// }
    ///   </code>
    /// </example>
    ///
    ///////////////////////////////////////////////////////////////////////////

    [AttributeUsage(AttributeTargets.Property, Inherited=true, AllowMultiple=false)]
    public sealed class RegexRuleAttribute:
        RuleAttribute
    {

        /// <summary>Constructor.</summary>
        /// <param name="pattern">The regular expression pattern to match.</param>
        public RegexRuleAttribute(string pattern):
            base(typeof(RegexRule<>))
        {
            _Pattern=pattern;
        }

        /// <summary>Gets the rule associated with the attribute.</summary>
        public override IRule GetRule(Type targetType, string propertyName)
        {
            if (string.IsNullOrEmpty(Description))
                return (IRule)typeof(RegexRule<>)
                    .MakeGenericType(targetType)
                    .GetConstructor(new Type[] { typeof(string), typeof(string), typeof(RegexOptions) })
                    .Invoke(new object[] { propertyName, Pattern, Options });
            else
                return (IRule)typeof(RegexRule<>)
                    .MakeGenericType(targetType)
                    .GetConstructor(new Type[] { typeof(string), typeof(string), typeof(string), typeof(RegexOptions) })
                    .Invoke(new object[] { propertyName, Description, Pattern, Options });
        }

        /// <summary>The regular expression pattern to match.</summary>
        public string Pattern
        {
            get
            {
                return _Pattern;
            }
        }

        /// <summary>A bitwise OR combination of <see cref="RegexOptions" /> enumeration values.</summary>
        public RegexOptions Options
        {
            get
            {
                return _Options;
            }
            set
            {
                _Options=value;
            }
        }

        /// <summary>The regular expression pattern to match.</summary>
        private string _Pattern;
        /// <summary>A bitwise OR combination of <see cref="RegexOptions" /> enumeration values.</summary>
        private RegexOptions _Options=RegexOptions.None;

    }
}
